#ifndef _PARAMS_HOLDER_H_
#error "This file must be included only from ParamsHoder.h"
#endif


#include <boost/lexical_cast.hpp>

namespace sd
{
namespace tools
{
namespace params_holder
{
namespace impl
{


struct EmptyType{};
typedef std::map<Param, Param> ParamsMap;


template< Param::Type tp >
struct wraper
{
	typedef boost::mpl::vector< EmptyType, int, double, std::string, ParamsMap > ParamsType;
	typedef typename boost::mpl::at< ParamsType, boost::mpl::int_<tp> >::type type;
	enum{ typeId = tp };
};

template< typename T >
struct findTypeWraper
{
	enum{ value = -1 };
};

template<>
struct findTypeWraper< EmptyType >
{
	enum{ value = Param::Empty };
};


template<>
struct findTypeWraper< int >
{
	enum{ value = Param::Int };
};


template<>
struct findTypeWraper< double >
{
	enum{ value = Param::Double };
};


template<>
struct findTypeWraper< std::string >
{
	enum{ value = Param::String };
};


template<>
struct findTypeWraper< const char* >
{
	enum{ value = Param::String };
};

template< size_t size >
struct findTypeWraper< const char[size] >
{
	enum{ value = Param::String };
};


template<>
struct findTypeWraper< ParamsMap >
{
	enum{ value = Param::Map };
};



template<  typename Result, typename Functor, typename Value >
Result executor( Param::Type type, Functor functor, Value& value )
{
	switch(type)
	{
	case Param::Int: return functor( wraper<Param::Int>(),value);break;
	case Param::String:return functor( wraper<Param::String>(),value);break;
	case Param::Double:return functor( wraper<Param::Double>(),value);break;
	case Param::Map:return functor( wraper<Param::Map>(),value);break;
	default:return functor( wraper<Param::Empty>() ,value);
	};
};


template< typename Wraper >
struct holder
{
	typedef typename Wraper::type type;
	holder( void*& ptr ): ptr_( *reinterpret_cast<type**>(&ptr) ){}
	void destroy(){ delete ptr_; ptr_ = 0; }
	void create(){ ptr_ = new type; }
	void create(const type& ref){ ptr_ = new type(ref); }
	template< typename T>
	void create(const T&){ assert(0); }
	void duplicate(const void* ptrRef){ ptr_ = new type( *(const type*)ptrRef ); }
	type& get(){ return *ptr_; };
	const type& get()const{ return *ptr_; };
private:
	type *& ptr_;
};






struct destroyer
{
	template< class Wraper >
	void operator()( Wraper, void*& buffer)
	{
		void* ptr = buffer;
		impl::holder<Wraper>(buffer).destroy();
		buffer = 0;
	}
};

template< typename T >
struct creator
{
	creator(const T& ref):ref_(ref){}

	template< class Wraper >
	void operator()( Wraper, void*& buffer)
	{
		impl::holder<Wraper>(buffer).create(ref_);
	}
	const T& ref_;
};



struct creator2
{
	template< class Wraper >
	void operator()( Wraper, std::pair<void**,const void*> ref)
	{
		impl::holder<Wraper>(*ref.first).duplicate(ref.second);
	}
};



template< typename T >
struct geter
{
	template< class Wraper >
	T& operator()( Wraper, void*& buffer )
	{
		return *(T*)(void*)&impl::holder<Wraper>(buffer).get();
	}
};

struct caster
{

	template< typename TFrom, typename TTo >
	struct caster3
	{
		void operator()( const TFrom& from, TTo& to )
		{
			try
			{
				to = boost::lexical_cast<TTo>(from);
			}catch(const boost::bad_lexical_cast&)
			{
				to = TTo();
			}
		}
	};


	template< typename TFrom >
	struct caster3<TFrom, ParamsMap>
	{
		void operator()( const TFrom& from, ParamsMap& to ){}
		//to = boost::lexical_cast<TTo>(from);
	};


	template< typename TTo >
	struct caster3 <ParamsMap, TTo>
	{
		void operator()( const ParamsMap& from, TTo& to ){}
		//to = boost::lexical_cast<TTo>(from);
	};

	template< typename TTo >
	struct caster3 <TTo, TTo>
	{
		void operator()( const TTo& from, TTo& to ){ assert(0); }

	};

	template<>
	struct caster3 <ParamsMap, ParamsMap>
	{
		void operator()( const ParamsMap& from, ParamsMap& to ){ assert(0); }

	};

	template< typename TTo >
	struct caster3 <EmptyType, TTo>
	{
		void operator()( const EmptyType& from, TTo& to ){}
		//to = boost::lexical_cast<TTo>(from);
	};

	template< typename TFrom >
	struct caster3<TFrom, EmptyType>
	{
		void operator()( const TFrom& from, EmptyType& to ){}
		//to = boost::lexical_cast<TTo>(from);
	};


	template<>
	struct caster3 <EmptyType, EmptyType>
	{
		void operator()( const EmptyType& from, EmptyType& to ){ assert(0); }

	};

	template<>
	struct caster3 <EmptyType, ParamsMap>
	{
		void operator()( const EmptyType& from, ParamsMap& to ){ assert(0); }

	};

	template<>
	struct caster3 <ParamsMap, EmptyType>
	{
		void operator()( const ParamsMap& from, EmptyType& to ){ assert(0); }

	};



	template< typename TFrom >
	struct caster2
	{
		template< class Wraper >
		void operator()( Wraper, void*& buffer)
		{
			typedef Wraper::type TTo;
			void* newBuffer;

			impl::holder<TFrom> oldval(buffer);
			impl::holder<Wraper> newval(newBuffer);
			newval.create();
			caster3< TFrom::type, Wraper::type > cast;
			cast( oldval.get(), newval.get() );
			oldval.destroy();
			buffer = newBuffer;
		}

	};

	caster(Param::Type toId):toId_(toId){}

	template< class Wraper >
	void operator()( Wraper, void*& buffer)
	{
		impl::executor<void>( toId_, caster2<Wraper>(), buffer );
	}

	Param::Type toId_;	
};


struct compare
{
	template< typename T >
	int operator()( T, const std::pair<void*,void*>& pair )
	{
		holder< T > first(const_cast< void* >(pair.first) );
		holder< T > second(const_cast< void* >(pair.second) );
		if( pair.first < pair.second )
			return -1;
		else
			if( pair.second < pair.first )
				return 1;
			else
				return 0;
	}
};


}//impl

void Param::drop()
{
	if( type_ != Empty )
	{
		impl::executor<void>( type_, impl::destroyer(), buffer_ );
		type_ = Empty;
	}
};



template< typename T>
Param& Param::operator=(const T& obj)
{
	Param dymmy(obj);
	swap(dymmy);
	return *this;
}


template< typename T>
Param::Param(const T& param)
{
	static_assert(impl::findTypeWraper<T>::value!=-1,"Error type");
	typedef  impl::wraper< (Param::Type)impl::findTypeWraper<T>::value>::type naturalType;
	type_ =  (Param::Type)impl::findTypeWraper<T>::value;
	impl::executor<void>( type_ , impl::creator<naturalType>(param), buffer_ );
}

void Param::setType(Type type)
{
	impl::executor<void>( type_ , impl::caster(type), buffer_ );	
	type_ = type;
}


void Param::swap(Param& ref)
{
	void* buf = buffer_;
	buffer_ = ref.buffer_;
	ref.buffer_ = buf;

	Type tp = type_;
	type_ = ref.type_;
	ref.type_ = tp;
}


Param& Param::operator[](const Param& key)
{
	static Param dymmy;

	if( type_ != Map )
	{
		static Param one = 1;
		if( key == one )
			return *this;
		else
			setType( Map );
	}
	impl::holder< impl::wraper<Map> > val(buffer_);
	return val.get()[key];
}


const Param& Param::operator[](const Param& key)const
{
	static Param dymmy;

	if( type_ == Map )
	{
		impl::holder< impl::wraper<Map> > val(const_cast< void* >(buffer_) );
		impl::ParamsMap::iterator it = val.get().find( key );
		if( it == val.get().end() )
			return dymmy;
		else
			return *this;
	}else
	{
		static Param one = 1;
		return key==one ? *this : dymmy;
	}
}


int Param::equal(const Param& ref)const
{
	if( ref.type_ == type_ )
	{
		return impl::executor<int>( type_ , impl::compare(), std::make_pair(buffer_, ref.buffer_) );	
	}else
	{
		if(ref.type_ > type_ )
		{
			Param dymmy( *this );
			dymmy.setType( ref.type_ );
			return dymmy.equal( ref );
		}else
		{
			Param dymmy( ref );
			dymmy.setType( type_ );
			return equal( dymmy );
		}
	}
}


Param::Param(const Param& ref)
{
	type_ = ref.type_;
	impl::executor<void>( type_ , impl::creator2(), std::make_pair(&buffer_, ref.buffer_) );	
}

template< typename T>
Param::operator T()const
{
	static_assert(impl::findTypeWraper<T>::value!=-1,"Error type");
	typedef  impl::wraper< (Param::Type)impl::findTypeWraper<T>::value>::type naturalType;

	if( impl::findTypeWraper<T>::value == type_ )
	{
		return impl::executor<T>( type_ , impl::geter<naturalType>(), (void*&)buffer_ );	
	}else
	{
		Param dymmy( *this );
		dymmy.setType( (Param::Type)impl::findTypeWraper<T>::value );
		return (naturalType)dymmy;
	}
}



}//params_holder
}//tools
}//sd
